/**
 * CardFlipScene
 * A CardFlipScene is a Sprite containing a Papervision3D scene with
 * two sides of a plane that can flip on its Y axis.
 *
 * @author		Craig Phares
 * @version		2.0
 */
package com.rad.papervision3d {
	
	import caurina.transitions.Tweener;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import org.papervision3d.events.InteractiveScene3DEvent;
	import org.papervision3d.materials.BitmapMaterial;
	import org.papervision3d.objects.primitives.Plane;
	
	public class CardFlipScene extends Sprite {
	
		private var _side1_mc:Sprite;			// the front of the card sprite
		private var _side2_mc:Sprite;			// the back of the card sprite
		private var _scene:PapervisionScene;	// the papervision3D scene
		private var _plane1:Plane;				// the front of the card plane
		private var _plane2:Plane;				// the back of the card plane
		private var _isFlipped:Boolean;			// if the card is on the back side
		private var _isFlipping:Boolean;		// if the card is flipping
		private var _isInteractive:Boolean;		// if the card is clickable
	
		/**
		 * Creates an instance of the CardFlipScene class.
		 * Requires 2 sprites, a front and back.
		 *
		 * @param side1			the front side of the card to be used as a texture
		 * @param side2			the back side of the card to be used as a texture
		 * @param interactive	if the card should be clickable
		 */
		public function CardFlipScene(side1:Sprite, side2:Sprite, interactive:Boolean = false) {
			
			// save the source sprites
			_side1_mc = side1;
			_side2_mc = side2;
			
			// save interactive flag
			_isInteractive = interactive;
			
			// determine the sprite dimensions
			var planeWidth:Number = _side1_mc.width;
			var planeHeight:Number = _side1_mc.height;
			
			// calculate the scene size
			var sceneWidth:Number = planeWidth * 2;
			var sceneHeight:Number = planeHeight * 2;
			
			// create the scene
			_scene = new PapervisionScene(sceneWidth, sceneHeight);
			_scene.camera.zoom = 115;
			_scene.x = -sceneWidth / 2;
			_scene.y = -sceneHeight / 2;
			addChild(_scene);
			
			// capture the first material
			var bmp1:BitmapData = new BitmapData(planeWidth, planeHeight, true, 0);
			bmp1.draw(_side1_mc);
			var material1:BitmapMaterial = new BitmapMaterial(bmp1);
			material1.interactive = true;
			material1.smooth = true;
			
			// create the front side
			_plane1 = new Plane(material1, planeWidth, planeHeight, 10, 10);
			_scene.addToScene(_plane1);
			
			// capture the second material
			var bmp2:BitmapData = new BitmapData(planeWidth, planeHeight, true, 0);
			bmp2.draw(_side2_mc);
			var material2:BitmapMaterial = new BitmapMaterial(bmp2);
			material2.interactive = true;
			material2.smooth = true;
			
			// create the back side
			_plane2 = new Plane(material2, planeWidth, planeHeight, 10, 10);
			_plane2.rotationY = 90;
			_scene.addToScene(_plane2);
			
			// add rollovers to the planes
			if (_isInteractive) var ip1:InteractiveObject = new InteractiveObject(_plane1, _scene.viewport);
			if (_isInteractive) var ip2:InteractiveObject = new InteractiveObject(_plane2, _scene.viewport);

			// add listeners for clicks
			_plane1.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);
			_plane2.addEventListener(InteractiveScene3DEvent.OBJECT_CLICK, onObjectClick);
			
		}
		
		/**
		 * get the current card object (read only)
		 *
		 * @return	the Papervision3D Plane primitive used for the card
		 */
		public function get card():Plane {
			if (_isFlipped) return _plane2;
			else return _plane1;
		}
		
		/**
		 * flip to the opposite side
		 *
		 * @param delay		the time in seconds to delay the flip
		 * @param time		the duration of the flip in seconds 
		 * @param clockwise	if the rotation should be in the clickwise direction
		 */
		public function flip(delay:Number = 0, time:Number = 1, clockwise:Boolean = true):void {
			if (_isFlipped) flipToFront(delay, time, clockwise);
			else flipToBack(delay, time, clockwise);
		}
		
		/**
		 * flip to the back
		 *
		 * @param delay		the time in seconds to delay the flip
		 * @param time		the duration of the flip in seconds 
		 * @param clockwise	if the rotation should be in the clickwise direction
		 */
		public function flipToBack(delay:Number = 0, time:Number = 1, clockwise:Boolean = true):void {
			
			_isFlipping = true;
			
			// determine the time for half a flip
			var halfTime:Number = time / 2;

			// setup start and end rotations based on spin direction
			var rotBegin1:Number = 0;
			var rotEnd1:Number = -90;
			var rotBegin2:Number = 90;
			var rotEnd2:Number = 0;
			if (clockwise) {
				rotEnd1 = 90;
				rotBegin2 = -90;
			}
			
			// snap planes to start rotations
			_plane1.rotationY = rotBegin1;
			_plane2.rotationY = rotBegin2;
			
			// animate the planes
			Tweener.addTween(_plane1, {rotationY:rotEnd1, delay:delay, time:halfTime, transition:"linear"});
			Tweener.addTween(_plane2, {rotationY:rotEnd2, delay:(delay + halfTime), time:halfTime, transition:"linear", onComplete:finishFlip});
			
			// save the flip state
			_isFlipped = true;
			
		}
		
		/**
		 * flip to the front
		 *
		 * @param delay		the time in seconds to delay the flip
		 * @param time		the duration of the flip in seconds 
		 * @param clockwise	if the rotation should be in the clickwise direction
		 */
		public function flipToFront(delay:Number = 0, time:Number = 1, clockwise:Boolean = true):void {
			
			_isFlipping = true;
			
			// determine the time for half a flip
			var halfTime:Number = time / 2;

			// setup start and end rotations based on spin direction
			var rotBegin1:Number = 90;
			var rotEnd1:Number = 0;
			var rotBegin2:Number = 0;
			var rotEnd2:Number = -90;
			if (clockwise) {
				rotBegin1 = -90;
				rotEnd2 = 90;
			}
			
			// snap planes to start rotations
			_plane1.rotationY = rotBegin1;
			_plane2.rotationY = rotBegin2;
			
			// animate the planes
			Tweener.addTween(_plane2, {rotationY:rotEnd2, delay:delay, time:halfTime, transition:"linear"});
			Tweener.addTween(_plane1, {rotationY:rotEnd1, delay:(delay + halfTime), time:halfTime, transition:"linear", onComplete:finishFlip});
			
			// save the flip state
			_isFlipped = false;
			
		}
		
		/**
		 * finish the flip motion
		 */
		private function finishFlip():void {
			_isFlipping = false;
		}

		/**
		 * dispatch a click event
		 *
		 * @param e		the interactive scene event
		 */
		private function onObjectClick(e:InteractiveScene3DEvent):void {
			if (!_isFlipping && _isInteractive) dispatchEvent(new InteractiveScene3DEvent(e.type));
		}
		
	}
	
}